package app

import (
	"os"
	"os/signal"
	"syscall"
	"fmt"
	"context"
	"net/http"
	"sync"
	"time"
	"gitee.com/zhucheer/orange/cfg"
	"gitee.com/zhucheer/orange/logger"
)

type StopSignal int32

type exitWait struct {
	mutex sync.Mutex
	wg *sync.WaitGroup
	deferFuns []func()
	stopSignList []chan StopSignal
}

var exitWaitHandler *exitWait

func init(){
	exitWaitHandler = &exitWait{
		wg: &sync.WaitGroup{},
	}
}

// ExitWaitFunDo 退出后等待处理完成
func ExitWaitFunDo(doFun func()){
	exitWaitHandler.wg.Add(1)
	defer exitWaitHandler.wg.Done()
	if doFun != nil{
		doFun()
	}
}

// AppDefer 应用退出后置操作
func AppDefer(deferFun ...func()){
	exitWaitHandler.mutex.Lock()
	defer exitWaitHandler.mutex.Unlock()
	for _,funcItem:=range deferFun{
		if funcItem != nil{
			exitWaitHandler.deferFuns=append(exitWaitHandler.deferFuns, funcItem)
		}
	}
}

// ListenStop 订阅app退出信号
func ListenStop(stopSig chan StopSignal){
	exitWaitHandler.mutex.Lock()
	defer exitWaitHandler.mutex.Unlock()

	exitWaitHandler.stopSignList = append(exitWaitHandler.stopSignList, stopSig)
}



// listenShutDownSign 监听推出信号
func listenShutDownSign(ctx context.Context, httpSrv *http.Server){

	exitSig := []os.Signal{
		syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGTERM,
	}

	appsign := make(chan os.Signal, 1)
	signal.Notify(appsign, exitSig...)

	select {
		case <-appsign:
			fmt.Println("[ORANGE] \033[0;33m app shutdown sign \033[0m ")
			// 发送应用退出信号
			sendAppStop()

			//处理后置操作等待结束
			appDeferDo(ctx)

			// 关闭http服务
			httpSrv.Shutdown(ctx)
	}
}

// sendAppStop 发送应用退出信号
func sendAppStop(){

	for _,stopSigItem:=range exitWaitHandler.stopSignList{
		go func(sig chan StopSignal) {
			sig<-StopSignal(1)
		}(stopSigItem)
	}
}

// appDefer 退出后处理
func appDeferDo(ctx context.Context){
	go func() {
		maxTimeout:=cfg.Config.GetInt("app.maxWaitSecond")
		if maxTimeout == 0{
			maxTimeout = 90
		}
		maxTimeoutContext,_:= context.WithTimeout(ctx, time.Duration(maxTimeout)*time.Second)
		select{
			case <-maxTimeoutContext.Done():
				fmt.Println(fmt.Sprintf("[ORANGE] \033[0;33m app wait %v timeout shutdown \033[0m ", maxTimeout))
				logger.Critical("app server shutdown by timeout")
				os.Exit(0)
		}
	}()

	for _,funcItem:=range exitWaitHandler.deferFuns{
		exitWaitHandler.wg.Add(1)
		funcItem()
		exitWaitHandler.wg.Done()
	}

}